﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;
using AMS.DataAccess.Common;
using AMS.DataAccess.Properties;

namespace AMS.DataAccess
{
    /// <summary>
    /// 一个 DataTable 与业务对象互相转换的辅助。
    /// 业务对象的要操作的属性必需要有 DBAttribute 子类的标注
    /// </summary>
    public class BOMapper
    {
        /// <summary>
        /// 将DataTable转换成与之对应的业务对象列表。
        /// </summary>
        /// <typeparam name="T">业务实体，指有DBTableAttribute修饰的实体。</typeparam>
        /// <param name="table">数据库表</param>
        /// <returns>业务对象列表</returns>
        public List<T> DataTableToBOList<T>(DataTable table)
        {
            if (table == null)
            {
                return null;
            }

            var map = MakeColumnMap<T>(table);
            List<T> list = new List<T>();
            foreach (DataRow row in table.Rows)
            {
                list.Add(DataRowToBOInternal<T>(row, map));
            }

            return list;
        }

        /// <summary>
        /// 将一个DataRow转换成一个业务实体对象。
        /// </summary>
        /// <typeparam name="T">业务实体，指有DBTableAttribute修饰的实体。</typeparam>
        /// <param name="row">要转换的数据列</param>
        /// <returns>业务实体对象</returns>
        public T DataRowToBO<T>(DataRow row)
        {
            var map = MakeColumnMap<T>(row.Table);
            return DataRowToBOInternal<T>(row, map);
        }

        /// <summary>
        /// 找到 DataTable 中有与 type 对应列，并构造成一个索引表，Key->columnName, Value->columnIndex
        /// </summary>
        /// <param name="table">要建立关系的数据表</param>
        /// <returns>一个索引表，Key->columnName, Value->columnIndex</returns>
        private Dictionary<string, int> MakeColumnMap<T>(DataTable table)
        {
            return MakeColumnMap(table, typeof(T));
        }

        /// <summary>
        /// 找到 DataTable 中有与 type 对应列，并构造成一个索引表，Key->columnName, Value->columnIndex
        /// </summary>
        /// <param name="table">要建立关系的数据表</param>
        /// <param name="type">要建立关系的对象</param>
        /// <returns>一个索引表，Key->columnName, Value->columnIndex</returns>
        private Dictionary<string, int> MakeColumnMap(DataTable table, Type type)
        {
            ObjectInfo objectInfo = ObjectUtils.GetBOObjectInfo(type);
            Dictionary<string, int> map = new Dictionary<string, int>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                string columnName = table.Columns[i].ColumnName;
                string fieldName = FindMappingField(objectInfo, columnName);
                if (!string.IsNullOrEmpty(fieldName))
                {
                    map.Add(fieldName, i);
                }
            }

            return map;
        }

        /// <summary>
        /// 查找映射的字段名
        /// </summary>
        /// <param name="objectInfo"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        protected virtual string FindMappingField(ObjectInfo objectInfo, string columnName)
        {
            string fieldName = null;
            Dictionary<string, PropertyInfo> dict = objectInfo.DBFields;
            foreach (string key in dict.Keys)
            {
                // 由于需要不分大小地比较
                if (string.Equals(key, columnName, StringComparison.OrdinalIgnoreCase))
                {
                    fieldName = key;
                    break;
                }
            }
            return fieldName;
        }

        /// <summary>
        /// 将一个DataRow转换成一个业务实体对象，根据已经构造好的列名与列索引的对应关系表
        /// </summary>
        /// <typeparam name="T">业务对象类型</typeparam>
        /// <param name="row">数据列</param>
        /// <param name="columnNameToIndexMap">列名与列索引的对应关系表</param>
        /// <returns>转换后业务对象</returns>
        private T DataRowToBOInternal<T>(DataRow row, Dictionary<string, int> columnNameToIndexMap)
        {
            ObjectInfo objectInfo = ObjectUtils.GetBOObjectInfo<T>();
            T obj = Activator.CreateInstance<T>();
            foreach (KeyValuePair<string, int> item in columnNameToIndexMap)
            {
                PropertyInfo property = objectInfo.DBFields[item.Key];
                SetValue<T>(obj, property, row[item.Value]);
            }

            return obj;
        }

        /// <summary>
        /// 根据类型获取业务对象的信息。主要是获取与DBAttribute相关的信息
        /// 在实现上使用缓存机制。但未进行同步控制。
        /// </summary>
        /// <param name="objectType">业务对象的类型</param>
        /// <returns>业务的信息</returns>
        private ObjectInfo GetBOObjectInfo(Type objectType)
        {
            ObjectInfo objectInfo = ObjectInfoCache.GetObject(objectType);
            if (objectInfo == null)
            {
                objectInfo = new ObjectInfo(objectType);
                objectInfo.BuildObject();
                ObjectInfoCache.AddObjectToCache(objectType, objectInfo);
            }

            return objectInfo;
        }

        /// <summary>
        /// 获取类型的信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private ObjectInfo GetBOObjectInfo<T>()
        {
            return GetBOObjectInfo(typeof(T));
        }

        /// <summary>
        /// 隐式设置一个属性的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        private void SetValue<T>(T obj, PropertyInfo property, object value)
        {
            MethodInfo method = property.GetSetMethod();
            if (method == null)
            {
                return;
            }

            if (property.PropertyType.IsSubclassOf(typeof(Enum)))
            {
                object[] attributes = property.GetCustomAttributes(typeof(DBEnumColumnAttribute), true);
                if (attributes.Length == 0)
                {
                    throw new AttributeException(string.Format(Resources.DBEnumColumnAttributeRequiredFormat, property.Name));
                }

                DBEnumColumnAttribute enumAttr = (DBEnumColumnAttribute)attributes[0];
                if (enumAttr.EnumType != null)
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToEnum(value, enumAttr.EnumType) });
                }
                else
                {
                    method.Invoke(obj, new object[] { AdoConverter.ToEnum(value, property.PropertyType) });
                }
            }
            else
            {
                ObjectUtils.SetValue<T>(obj, property, value);
            }
        }

        /// <summary>
        /// 将一个DataTable转换成一个对象的 List
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public List<int> DataTableToIntList(DataTable table)
        {
            return DataTableToIntList(table, null);
        }

        /// <summary>
        /// 将一个DataTable的某个列的所有内容转换成一个int数组
        /// </summary>
        /// <param name="table"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public List<int> DataTableToIntList(DataTable table, string column)
        {
            int columnIndex = 0;
            if (!string.IsNullOrEmpty(column))
            {
                columnIndex = table.Columns.IndexOf(column);
            }

            if (columnIndex == -1)
            {
                throw new ArgumentException(string.Format(Resources.ColumnNotExistFormat, column));
            }

            List<int> list = new List<int>();
            foreach (DataRow row in table.Rows)
            {
                list.Add(AdoConverter.ToInt32(row[columnIndex]));
            }

            return list;
        }

        /// <summary>
        /// 将一个DataTable的第 0 列的所有内容转换成一个int数组
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public List<string> DataTableToStringList(DataTable table)
        {
            return DataTableToStringList(table, null);
        }

        /// <summary>
        /// 将一个DataTable的某个列的所有内容转换成一个int数组
        /// </summary>
        /// <param name="table"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public List<string> DataTableToStringList(DataTable table, string column)
        {
            int columnIndex = 0;
            if (!string.IsNullOrEmpty(column))
            {
                columnIndex = table.Columns.IndexOf(column);
            }

            if (columnIndex == -1)
            {
                throw new ArgumentException(string.Format(Resources.ColumnNotExistFormat, column));
            }

            List<string> list = new List<string>();
            foreach (DataRow row in table.Rows)
            {
                list.Add(AdoConverter.ToString(row[columnIndex]));
            }

            return list;
        }

        /// <summary>
        /// 将对象的值转到DataRow对应的值中
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bo"></param>
        /// <param name="row"></param>
        public void BOToDataRow<T>(T bo, DataRow row)
        {
            Dictionary<string, int> map = MakeColumnMap<T>(row.Table);
            BOToDataRowInternal<T>(bo, row, map);
        }

        /// <summary>
        /// 将对象的值转到DataRow对应的值中，使用给定的映射
        /// </summary>
        /// <typeparam name="T">业务对象类型</typeparam>
        /// <param name="bo">业务对象</param>
        /// <param name="row">数据行</param>
        /// <param name="columnNameToIndexMap">列名与列索引的对应关系表</param>
        private void BOToDataRowInternal<T>(T bo, DataRow row, Dictionary<string, int> columnNameToIndexMap)
        {
            ObjectInfo objectInfo = GetBOObjectInfo<T>();
            foreach (KeyValuePair<string, int> item in columnNameToIndexMap)
            {
                int columnIndex = columnNameToIndexMap[item.Key];
                PropertyInfo property = objectInfo.DBFields[item.Key];

                object value = property.GetValue(bo, null);
                row[columnIndex] = value;
            }
        }

        /// <summary>
        /// 业务对象列表映射到DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="boList"></param>
        /// <param name="table"></param>
        public void BOListToRows<T>(List<T> boList, DataTable table)
        {
            Dictionary<string, int> map = MakeColumnMap<T>(table);
            foreach (T bo in boList)
            {
                DataRow row = table.NewRow();
                BOToDataRowInternal<T>(bo, row, map);
                table.Rows.Add(row);
            }
        }
    }
}
